/***************************************************************************
 *   Copyright (C) 2006 by Harish Kukreja   *
 *   harish.kukreja@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

//
// C++ Implementation: collision
//
// Description: Definitions of collision detection & reaction functions.
//
//
// Author: Harish Kukreja <harish.kukreja@gmail.com>, (C) 2006
//
// Copyright: See COPYING file that comes with this distribution
//
//

#include <physiksbase/collision.h>


using PhysiksBase::Computation::compute_distance;
using PhysiksBase::Computation::compute_connecting_vector;

// Collision detection functions //////////////////////////////////////////////
bool PhysiksBase::Collisions::detect_collisions(const World &world) {
    for(uint i = 0; i < world.num_objects(); i++) {
        // Perhaps one could do a pairwise comparison, by calling one of the detect_* functions
        // depending upon each object's type.
    }
    return false;
}

///////////////////////////////////////////////////////////////////////////////
bool PhysiksBase::Collisions::detect_point_sphere(const PhysicalObject &obj, const Sphere &sph) {
    return compute_distance(obj, sph) < sph.get_radius();
}

///////////////////////////////////////////////////////////////////////////////
bool PhysiksBase::Collisions::detect_point_plane(const PhysicalObject &obj, const Plane &plane) {
    /** Find the minimum distance between the point and the plane. If the `base' of the plane-point
     * interconnect lies in the bounded region, return true. */
    Vector temp(obj.position - plane.position);
    Vector distance(plane.normal() * temp * plane.normal());
    if (!plane.bounded()) {
        return distance.norm() < 1.0E-3;    // This arbitrary value should not be hard-coded.
    } else {
        if (plane.contains(obj.position - distance)) return distance.norm() < 1.0E-3;
        else return false;
        // This doesn't take into account "skimming" collisions, where only a small portion of the
        // sphere may strike the edge of the bounded plane.
    }
}

///////////////////////////////////////////////////////////////////////////////
bool PhysiksBase::Collisions::detect_sphere_sphere(const Sphere &sph1, const Sphere &sph2) {
    return compute_distance(sph1, sph2) < (sph1.get_radius() + sph2.get_radius());
}

///////////////////////////////////////////////////////////////////////////////
bool PhysiksBase::Collisions::detect_sphere_plane(const Sphere &sph, const Plane &plane) {
    Vector temp = sph.position - plane.position;
    Vector distance = plane.normal() * temp * plane.normal();
    if (plane.bounded()) {
        // How does one account for bounded planes? Hmmm....
        // This doesn't take into account "skimming" collisions, where only a small portion of the
        // sphere may strike the edge of the bounded plane.
        if (plane.contains(sph.position - distance)) return distance.norm() < 1.0E-3;
        else return false;
    } else {
        /** Find the minimum distance between the centre of the sphere and the plane, and compare
         * it to the sphere's radius. */
        return distance.norm() < 1.0E-3;    // This arbitrary value should not be hard-coded.
    }
}

//=============================================================================

// Collision reaction functions ///////////////////////////////////////////////
void PhysiksBase::Collisions::react_point_sphere(PhysicalObject &obj, Sphere &sph) {
    obj.net_force += compute_connecting_vector(obj, sph) * sph.get_elasticity();
}

void PhysiksBase::Collisions::react_point_plane(PhysicalObject &obj, Plane &plane) {
}

void PhysiksBase::Collisions::react_sphere_sphere(Sphere &sph1, Sphere &sph2) {
    double dist = compute_distance(sph1, sph2);
    Vector temp = (sph2.position - sph1.position) * (1 / dist);
    sph1.net_force += temp * sph2.get_elasticity() * (sph2.get_radius() - dist);
    sph2.net_force -= temp * sph1.get_elasticity() * (sph1.get_radius() - dist);
}

void PhysiksBase::Collisions::react_sphere_plane(Sphere &sphere, Plane &plane) {
}
